<?php
namespace PsiFramework\Classes\Util\Database\QueryInterpreter;

import("Classes.Util.Database.QueryInterpreter.QueryInterpreterInterface.ISqlQueryInterpreter");
import("Classes.Util.Database.SqlQuery");

use \Exception;
use \PsiFramework\Classes\Util\Database\SqlQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlColumn;
use \PsiFramework\Classes\Util\Database\Query\SqlAddColumn;
use \PsiFramework\Classes\Util\Database\Query\SqlAlterTableQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlChangeColumnType;
use \PsiFramework\Classes\Util\Database\Query\SqlCreateTableQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlCreateTriggerQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlCreateViewQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlDeleteTableQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlDeleteViewQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlForeignKey;
use \PsiFramework\Classes\Util\Database\Query\SqlFunction;
use \PsiFramework\Classes\Util\Database\Query\SqlIndex;
use \PsiFramework\Classes\Util\Database\Query\SqlJoinClause;
use \PsiFramework\Classes\Util\Database\Query\SqlLimit;
use \PsiFramework\Classes\Util\Database\Query\SqlOn;
use \PsiFramework\Classes\Util\Database\Query\SqlOperation;
use \PsiFramework\Classes\Util\Database\Query\SqlOrder;
use \PsiFramework\Classes\Util\Database\Query\SqlPrimaryKey;
use \PsiFramework\Classes\Util\Database\Query\SqlParameter;
use \PsiFramework\Classes\Util\Database\Query\SqlTable;
use \PsiFramework\Classes\Util\Database\Query\SqlType;
use \PsiFramework\Classes\Util\Database\Query\SqlUniqueKey;
use \PsiFramework\Classes\Util\Database\Query\SqlUsing;
use \PsiFramework\Classes\Util\Database\Query\SqlValue;
use \PsiFramework\Classes\Util\Database\QueryInterpreter\QueryInterpreterInterface\ISqlQueryInterpreter;

/**
 * PsiFramework
 * Php Simple Framework
 *
 * @author Rémi San
 * @version beta
 */

/**
 * The SqlQueryInterpreter is used to translate SqlQueries to string queries
 *
 * @package Util.QueryInterpreter
 */
abstract class SqlQueryInterpreter implements ISqlQueryInterpreter
{

    protected /*SqlQuery*/ $_sqlQuery ;
    protected /*string[]*/ $_aliases ;

    public function __construct(SqlQuery $sqlQuery=null)
    {
        $this->_sqlQuery = $sqlQuery ;
        $this->_aliases = array();
    }

    protected abstract function getVarDelimiter();

    protected function getSchemaPrefixedTableName(SqlTable $table=null)
    {
        if (isNullOrEmptyString($table)) {
        	return null ;
        }
        
        $tableName = '' ;
        if (!is_null($table->getSchema())) {
            $tableName .= 
                $this->getVarDelimiter() . 
                $table->getSchema() . 
                $this->getVarDelimiter() . '.' ;
        }
        $tableName .=
            $this->getVarDelimiter() .
            $table->getName() .
            $this->getVarDelimiter();

        return $tableName ;
    }

    protected function getTable(SqlTable $table=null, /*boolean*/ $useAlias=true)
    {
        if (is_null($table)) {
            return null ;
        }

        $tableName = null ;
        if ($useAlias
            && !isNullOrEmptyString($table->getAlias())
            && in_array($table->getAlias(), $this->_aliases)
        ) {
            $tableName 
                = $this->getVarDelimiter() .
                $table->getAlias() .
                $this->getVarDelimiter();
        } else {
            $tableName = $this->getSchemaPrefixedTableName($table);
        }

        if (!isNullOrEmptyString($tableName)) {
            return $tableName ;
        } else {
            return null ;
        }
    }

    protected function getTableAsAlias(SqlTable $table=null)
    {
        if (isNullOrEmptyString($table)) {
            return null ;
        }

        $tableName = $this->getTable($table, false);
        if (!isNullOrEmptyString($tableName)
            && !isNullOrEmptyString($table->getAlias())
        ) {
            $alias = $table->getAlias();
            $tableName .=
                ' AS ' .
                $this->getVarDelimiter() .
                $alias .
                $this->getVarDelimiter();
            $this->_aliases[] = $alias ;
        }

        return $tableName ;
    }

    public abstract function getColumnType(
        SqlType  $sqlType=null,
        /*bool*/ $includeNullDeclaration=true,
        /*bool*/ $includeLength=true
    );

    protected function getColumnDescription(
        SqlColumn $column=null,
        /*bool*/  $includeNullDeclaration=true
    )
    {
        $columnDescr = $this->getColumn($column, false, false);
        $columnDescr .=
            ' ' . $this->getColumnType(
                $column->getType(), $includeNullDeclaration
            );
        
        return $columnDescr ;
    }

    protected function getColumn(
        SqlColumn   $column=null,
        /*boolean*/ $useAlias=true,
        /*boolean*/ $prefixWithTable=true
    )
    {
        if (is_null($column)) {
            return null ;
        }
        if (isNullOrEmptyString($column->getName())) {
            return null ;
        }

        $columnName = null ;
        if ($useAlias
            && !isNullOrEmptyString($column->getAlias())
            && in_array($column->getAlias(), $this->_aliases)
        ) {
            $columnName
                = $this->getVarDelimiter() .
                $column->getAlias() .
                $this->getVarDelimiter();
        } else {
            $columnName = '' ;

            if (!isNullOrEmptyString($column->getTable()) && $prefixWithTable) {
                $tableAlias = $this->getTable($column->getTable());
                if (!isNullOrEmptyString($tableAlias)) {
                    $columnName = $tableAlias . '.' ;
                }
            }

            if ($column->getName() == '*') {
                $columnName .= '*';
            } else {
                $columnName .=
                    $this->getVarDelimiter() .
                    $column->getName() .
                    $this->getVarDelimiter();
            }
        }

        if (!isNullOrEmptyString($columnName)) {
            if (!isNullOrEmptyString($column->getNewOld())) {
                $columnName = $column->getNewOld() . '.' . $columnName ;
            }
            return $columnName ;
        } else {
            return null ;
        }
    }

    protected function getFunction(
        SqlFunction $function=null,
        /*boolean*/ $useAlias=true
    )
    {
        if (is_null($function)) {
            return null ;
        }
        if (isNullOrEmptyString($function->getName())) {
            return null ;
        }

        $functionString = null ;
        if ($useAlias
            && !isNullOrEmptyString($function->getAlias())
            && in_array($function->getAlias(), $this->_aliases)
        ) {
            $functionString = $function->getAlias();
        } else {
            $functionString = $function->getName() . '(' ;
            if (!isNullOrEmptyString($function->getParameters())) {
                $first = true ;
                foreach ($function->getParameters() as $parameter) {
                    if (!$first) {
                        $functionString .= ', ' ;
                    }

                    $parameterAsString = $this->getParameter($parameter, false);
                    if (!isNullOrEmptyString($parameterAsString)) {
                        $functionString .= $parameterAsString ;
                        $first = false ;
                    }
                }
            }
            $functionString .= ')' ;
        }
        return $functionString ;
    }

    protected abstract function getSqlValue(/*mixed*/ $value);

    protected function getValue(SqlValue $value=null, /*boolean*/ $useAlias=true)
    {
        $valueString = null ;
        if ($useAlias
            && !isNullOrEmptyString($value)
            && !isNullOrEmptyString($value->getAlias())
            && in_array($value->getAlias(), $this->_aliases)
        ) {
            $valueString = $value->getAlias();
        } else {
            $realValue
                = (!isNullOrEmptyString($value))?
                    $value->getValue():$value ;
            $valueString = $this->getSqlValue($realValue);
        }
        return $valueString ;
    }

    protected function getStringOperator(/*string*/ $operator)
    {
        if (isNullOrEmptyString($operator)) {
            return null ;
        }
        return $operator ;
    }

    protected function getOperation(
        SqlOperation $operation=null,
        /*boolean*/  $useAlias=true,
        /*boolean*/  $prefixWithTable=true,
        /*boolean*/  $equalLikeIsForNull=false
    )
    {
        if (isNullOrEmptyString($operation)) {
            return null ;
        }
        if (isNullOrEmptyString($operation->getOperator())) {
            return null ;
        }

        $operationParameters = $operation->getParameters();
        if (isNullOrEmptyString($operationParameters)) {
            return null ;
        }
        if (!is_array($operationParameters)) {
            return null ;
        }

        $operator = $this->getStringOperator($operation->getOperator());
        if (isNullOrEmptyString($operator)) {
            return null ;
        }

        $operationString = null ;
        switch($operator) {
            case SqlOperation::$equal :
            case SqlOperation::$like :
            case SqlOperation::$greaterThan :
            case SqlOperation::$greaterThanOrEqual :
            case SqlOperation::$lowerThan :
            case SqlOperation::$lowerThanOrEqual :
            case SqlOperation::$different :
            case SqlOperation::$in :
                if (count($operationParameters) != 2) {
                    return null ;
                }
    
                $firstParamValue = $operationParameters[0] ;
                if (isNullOrEmptyString($firstParamValue)) {
                    return null ;
                }
    
                $secondParamValue = $operationParameters[1] ;
    
                $firstParam = $this->getParameter(
                    $firstParamValue,
                    $useAlias,
                    $prefixWithTable
                );
                if (isNullOrEmptyString($firstParam)
                    || mb_strtoupper($firstParam, 'utf-8') == "NULL"
                ) {
                    return null ;
                }
    
                $secondParam = $this->getParameter(
                    $secondParamValue,
                    $useAlias,
                    $prefixWithTable
                );
                if (isNullOrEmptyString($secondParam)) {
                    return null ;
                }
    
                if ($equalLikeIsForNull
                    && mb_strtoupper($secondParam, 'utf-8') == "NULL"
                ) {
                    if ($operator == SqlOperation::$equal) {
                        $operator = "IS" ;
                    } else if ($operator == SqlOperation::$different) {
                        $operator = "IS NOT" ;
                    } else {
                        return null ;
                    }
                } else if ($operator == SqlOperation::$in) {
                    $secondParam = '('.$secondParam.')';
                }
                
                $operationString
                    = $firstParam . ' ' .
                    $operator . ' ' .
                    $secondParam ;
                break ;
            case SqlOperation::$isNull :
            case SqlOperation::$isNotNull :
                if (count($operationParameters) != 1) {
                    return null ;
                }
    
                $param = $this->getParameter(
                    $operationParameters[0],
                    $useAlias,
                    $prefixWithTable
                );
                if (isNullOrEmptyString($param)) {
                    return null ;
                }
    
                $operationString = $param . ' ' . $operator ;
                break;
            case SqlOperation::$and :
            case SqlOperation::$or :
                if (count($operationParameters) == 0) {
                    return null ;
                }
    
                $first = true ;
                foreach ($operationParameters as $param) {
                    $paramAsString = $this->getParameter(
                        $param,
                        $useAlias,
                        $prefixWithTable
                    );
    
                    if (!isNullOrEmptyString($paramAsString)) {
                        if ($first) {
                            $operationString = '(' ;
                        } else {
                            $operationString .=
                                ' ' .
                                $this->getStringOperator(
                                    $operation->getOperator()
                                ) .
                                ' ' ;
                        }
    
                        $operationString .= $paramAsString ;
                        $first = false ;
                    }
                }
                $operationString .= ')' ;
                break;
        }

        if (!isNullOrEmptyString($operationString)) {
            return $operationString ;
        } else {
            return null ;
        }
    }

    protected function getParameter(
        SqlParameter $parameter=null,
        /*boolean*/  $useAlias=true,
        /*boolean*/  $prefixWithTable=true,
        /*boolean*/  $equalLikeIsForNull=false
    )
    {
        if ($parameter instanceof SqlColumn) {
            return $this->getColumn($parameter, $useAlias, $prefixWithTable);
        } else if ($parameter instanceof SqlFunction) {
            return $this->getFunction($parameter, $useAlias);
        } else if ($parameter instanceof SqlOperation) {
            return $this->getOperation(
                $parameter,
                $useAlias,
                $prefixWithTable,
                $equalLikeIsForNull
            );
        } else if ($parameter instanceof SqlQuery) {
            return $this->querySwitch($parameter);
        } else {
            return  $this->getValue($parameter, $useAlias);
        }
    }

    protected function getParameterAsAlias(SqlParameter $parameter=null)
    {
        if (isNullOrEmptyString($parameter)) {
            return null ;
        }

        $columnName = $this->getParameter($parameter, false);
        if (!isNullOrEmptyString($columnName)
            && !isNullOrEmptyString($parameter->getAlias())
        ) {
            $alias = $parameter->getAlias();
            $columnName .=
                ' AS ' .
                $this->getVarDelimiter() .
                $alias .
                $this->getVarDelimiter();
            $this->_aliases[] = $alias ;
        }
        return $columnName ;
    }

    protected function getWhereClause(SqlParameter $parameter=null)
    {
        $whereClause = '' ;

        if (!isNullOrEmptyString($parameter)) {
            $params = $this->getParameter($parameter, false, true, true);
            if (!isNullOrEmptyString($params)) {
                $whereClause = 'WHERE ' . $params ;
            }
        }

        return $whereClause ;
    }

    protected function getGroupByClause(/*SqlColumn[]*/ $columns)
    {
        if (isNullOrEmptyString($columns)) {
            return '' ;
        }
        if (!is_array($columns)) {
            return '' ;
        }
        $groupByClause = null ;

        $first = true ;
        foreach ($columns as $column) {
            if (!isNullOrEmptyString($column)) {
                $columnAsString = '';
                if ($column instanceof SqlColumn) {
                    $columnAsString = $this->getColumn($column);
                } else if ($column instanceof SqlFunction) {
                    $columnAsString = $this->getFunction($column, false);
                }
                if (!isNullOrEmptyString($columnAsString)) {
                    if (!$first) {
                        $groupByClause .= ', ' ;
                    } else {
                        $groupByClause = '' ;
                    }
                    $groupByClause .= $columnAsString ;
                    $first = false ;
                }
            }
        }

        if (isNullOrEmptyString($groupByClause)) {
            return '' ;
        } else {
            return 'GROUP BY ' . $groupByClause ;
        }
    }

    protected function getAscDesc(/*string*/ $ascDesc)
    {
        if (isNullOrEmptyString($ascDesc)) {
            return null ;
        }
        return $ascDesc ;
    }

    protected function getOrder(SqlOrder $order=null)
    {
        if (is_null($order)) {
            return '' ;
        }
        if (is_null($order->getColumn())) {
            return '' ;
        }
        
        $column = $order->getColumn();
        
        $columnAsString = '';
        if ($column instanceof \PsiFramework\Classes\Util\Database\Query\SqlColumn) {
            $columnAsString = $this->getColumn($column);
        } else if ($column instanceof SqlFunction) {
            if (!isNullOrEmptyString($column->getAlias())) {
                $columnAsString = $this->getVarDelimiter().$column->getAlias().$this->getVarDelimiter();
            }
        }
        
        $ascDesc = $this->getAscDesc($order->getOrder());

        if (isNullOrEmptyString($columnAsString)) {
            return '' ;
        } else {
            $return = $columnAsString ;
            if (!isNullOrEmptyString($ascDesc)) {
                $return .= ' ' . $ascDesc ;
            }
            
            return $return ;
        }
    }

    protected function getOrderClause(/*SqlOrder[]*/ $orders)
    {
        
        if (is_null($orders) || !is_array($orders)) {
            return '' ;
        }
        $orderClause = '' ;

        $first = true ;
        foreach ($orders as $order) {
            if (!is_null($order) && $order instanceof \PsiFramework\Classes\Util\Database\Query\SqlOrder) {
                $orderAsString = $this->getOrder($order);
                if (!isNullOrEmptyString($orderAsString)) {
                    if (!$first) {
                        $orderClause .= ', ' ;
                    } else {
                        $orderClause = '' ;
                    }
                    $orderClause .= $orderAsString ;
                    $first = false ;
                }
            }
        }
        
        if (isNullOrEmptyString($orderClause)) {
            return '' ;
        } else {
            return 'ORDER BY ' . $orderClause ;
        }
    }

    protected abstract function getLimitClause(SqlLimit $limit=null);

    protected function getColumnsValuesList(/*SqlOperation[]*/ $parameters)
    {
        $columnsList = '' ;
        $valuesList = '' ;
        $first = true ;

        foreach ($parameters as $operation) {
            if ($operation instanceof SqlOperation) {
                if (!$first) {
                    $columnsList .= ', ' ;
                    $valuesList .= ', ' ;
                } else {
                    $first = false ;
                }

                $params = $operation->getParameters();

                $columnsList .= $this->getParameter($params[0], false, false);
                $valuesList .= $this->getParameter($params[1], false);
            }
        }
        return '(' .$columnsList . ') VALUES (' . $valuesList . ')' ;
    }

    protected function getSelectParameters(/*SqlParameter[]*/ $parameters)
    {
        if (isNullOrEmptyString($parameters)) {
            return '*' ;
        }
        if (!is_array($parameters)) {
            return '*' ;
        }
        $columns = null ;

        $first = true ;
        foreach ($parameters as $column) {
            if (!isNullOrEmptyString($column)) {
                $columnAsString = $this->getParameterAsAlias($column);
                if (!isNullOrEmptyString($columnAsString)) {
                    if (!$first) {
                        $columns .= ', ' ;
                    } else {
                        $columns = '' ;
                    }
                    $columns .= $columnAsString ;
                    $first = false ;
                }
            }
        }

        if (isNullOrEmptyString($columns)) {
            return '*' ;
        } else {
            return $columns ;
        }
    }

    protected function getJoinClause(SqlJoinClause $joinClause=null)
    {
        if (is_null($joinClause)) {
            return '' ;
        }

        if ($joinClause instanceof SqlOn) {
            $operation = $joinClause->getOperation();
            if (is_null($operation)) {
                return '' ;
            }
            $operationString = $this->getOperation($operation);
            if (isNullOrEmptyString($operationString)) {
                return '' ;
            }
            return 'ON (' . $operationString . ')' ;
        } else if ($joinClause instanceof SqlUsing) {
            $identifiers = $joinClause->getIdentifiers();
            $usingList = '' ;
            $first = true ;
            foreach ($identifiers as $identifier) {
                if (!$first) {
                    $usingList .= ', ' ;
                } else {
                    $first = false ;
                }
                $usingList .=
                    $this->getVarDelimiter() .
                    $identifier .
                    $this->getVarDelimiter();
            }
            if (isNullOrEmptyString($usingList)) {
                return '' ;
            }
            return 'USING (' . $usingList . ')' ;
        } else {
            throw new \Exception('Bad join clause !');
        }
    }

    protected function getFromClause(
        SqlTable      $table=null,
        /*SqlJoin[]*/ $joins=array()
    )
    {
        if (isNullOrEmptyString($table)) {
            throw new \Exception('Bad from clause !');
        }
        if (isNullOrEmptyString($table->getName())) {
            throw new \Exception('Bad from clause !');
        }
        if (!$table instanceof SqlTable) {
            throw new \Exception('Bad from clause !');
        }
        if (!is_array($joins)) {
            throw new \Exception('Bad from clause !');
        }

        $from = '' ;

        $tableAsString = $this->getTableAsAlias($table);
        if (isNullOrEmptyString($tableAsString)) {
            throw new \Exception('Bad from clause !');
        } else {
            $from = 'FROM ' . $tableAsString ;

            foreach ($joins as $join) {
                $joinTable = $join->getTable();
                $joinClause = $join->getJoinClause();

                $joinTableAsString = $this->getTableAsAlias($joinTable);
                if (isNullOrEmptyString($joinTableAsString)) {
                    throw new \Exception('Bad from clause !');
                }
                $innerOuter = ($join->isInner())?'':'OUTER' ;
                if (!is_null($joinClause)) {
                    $from .=
                        ' ' . $join->getJoinType() . ' ' .
                        $innerOuter . ' JOIN ' .
                        $joinTableAsString . ' ' .
                        $this->getJoinClause($joinClause);
                } else {
                    $from .= ', ' . $joinTableAsString ;
                }
            }
        }
        $from .= ' ' ;

        return $from;
    }

    protected function getSelectQuery(SqlQuery $query)
    {
        $stringQuery = 'SELECT' . ' ' ;

        //Select tables
        //Must be called before the columns to initialize the table aliases
        $from = $this->getFromClause(
            $query->getTable(),
            $query->getJoins()
        ) . ' ' ;

        //Select columns
        $stringQuery .=
            $this->getSelectParameters(
                $query->getParameters()
            ) . ' ' ;

        //append from
        $stringQuery .= $from . ' ' ;

        //WhereClause
        $stringQuery .=
            $this->getWhereClause(
                $query->getWhereClause()
            ) . ' ' ;

        //Group by
        $stringQuery .=
            $this->getGroupByClause(
                $query->getGroupByClauses()
            ) . ' ' ;

        //Order
        $stringQuery .= $this->getOrderClause($query->getOrderClauses()) . ' ' ;

        //Limit
        $stringQuery .= $this->getLimitClause($query->getLimit());

        return trim($stringQuery);
    }

    protected function getInsertQuery(SqlQuery $query)
    {
        $stringQuery = 'INSERT INTO' . ' ' ;

        //Table
        $stringQuery .= $this->getTable($query->getTable(), false) . ' ' ;

        //Values
        $stringQuery .= $this->getColumnsValuesList($query->getParameters());

        return trim($stringQuery);
    }

    protected function getUpdateQuery(SqlQuery $query)
    {
        $stringQuery = 'UPDATE' . ' ' ;

        //Table
        $stringQuery .= $this->getTable($query->getTable(), false) . ' ' ;

        //Values
        $stringQuery .= 'SET ' ;
        $first = true ;
        foreach ($query->getParameters() as $parameter) {
            if (!$first) {
                $stringQuery .= ', ' ;
            } else {
                $first = false ;
            }

            $stringQuery .= $this->getParameter($parameter, false, false);
        }
        $stringQuery .= ' ' ;

        //WhereClause
        $stringQuery .= $this->getWhereClause($query->getWhereClause());

        return trim($stringQuery);
    }

    protected abstract function getTableDefinitionSuffix();

    protected function getPrimaryKey(SqlPrimaryKey $index)
    {
        return $this->getKey($index, 'PRIMARY KEY');
    }

    protected function getUniqueKey(SqlUniqueKey $index)
    {
        return $this->getKey($index, 'UNIQUE');
    }

    protected function getKey(SqlIndex $index, /*string*/ $type)
    {

        $pk = $type . ' (' ;
        $first = true ;

        foreach ($index->getColumns() as $column) {
            if (!$first) {
                $pk .= ', ' ;
            } else {
                $first = false ;
            }

            $pk .= $this->getColumn($column, false, false);
        }

        $pk .= ')' ;

        return $pk ;
    }

    protected function getForeignKey(SqlForeignKey $index)
    {

        $reference = $index->getReference();

        $fk  = 'FOREIGN KEY' .
               ' (' .
                   $this->getColumn($index->getColumn(), false, false) .
               ') ' .
               'REFERENCES ' .
               $this->getTable($reference->getTable(), false) .
               '('.$this->getColumn($reference, false, false) . ')' ;

        return $fk ;
    }

    protected function getTableIndexes(/*SqlIndex[]*/ $indexes)
    {

        $indexString = '' ;
        $first = true ;

        foreach ($indexes as $index) {
            if (!$first) {
                $indexString .= ', ' ;
            } else {
                $first = false ;
            }

            if (!isNullOrEmptyString($index->getName())) {
                $indexString .=
                    'CONSTRAINT ' .
                    $this->getVarDelimiter() .
                    $index->getName() .
                    $this->getVarDelimiter() . ' ' ;
            }

            if ($index instanceof SqlPrimaryKey) {
                $indexString .= $this->getPrimaryKey($index);
            } else if ($index instanceof SqlForeignKey) {
                $indexString .= $this->getForeignKey($index);
            } else if ($index instanceof SqlUniqueKey) {
                $indexString .= $this->getUniqueKey($index);
            }
        }

        return $indexString ;
    }

    protected function getDeleteTableQuery(SqlDeleteTableQuery $query)
    {
        $stringQuery = 'DROP TABLE' . ' ' ;
        $stringQuery .= $this->getSchemaPrefixedTableName($query->getTable());

        return trim($stringQuery);
    }

    protected function getDeleteViewQuery(SqlDeleteViewQuery $query)
    {
        $stringQuery = 'DROP VIEW' . ' ' ;
        $stringQuery .= $this->getSchemaPrefixedTableName($query->getTable());

        return trim($stringQuery);
    }

    protected function getCreateTableQuery(SqlCreateTableQuery $query)
    {
        $stringQuery = 'CREATE TABLE' . ' ' ;
        $stringQuery .= $this->getSchemaPrefixedTableName($query->getTable());
        $stringQuery .= '(' ;

        $first = true ;
        foreach ($query->getParameters() as $column) {
            if (!$first) {
                $stringQuery .= ', ' ;
            } else {
                $first = false ;
            }

            $stringQuery .= $this->getColumnDescription($column);
        }
        
        $indexes = $this->getTableIndexes($query->getIndexes());
        
        if (!isNullOrEmptyString($indexes)) {
            $stringQuery .= ', ' . $indexes ;
        }

        $stringQuery .= ') ' . $this->getTableDefinitionSuffix() . ' ;' ;

        return trim($stringQuery);
    }

    protected function getCreateViewQuery(SqlCreateViewQuery $query)
    {
        $stringQuery = 'CREATE VIEW' . ' ' ;
        $stringQuery .= $this->getSchemaPrefixedTableName($query->getTable());
        $stringQuery .= ' AS ' ;
        $stringQuery .= $this->getSelectQuery($query->getSelectQuery());

        return trim($stringQuery);
    }

    protected function getDeleteQuery(SqlQuery $query)
    {
        $stringQuery = 'DELETE FROM' . ' ' ;

        //Table
        $stringQuery .= $this->getTable($query->getTable(), false) . ' ' ;

        //WhereClause
        $stringQuery .= $this->getWhereClause($query->getWhereClause());

        return trim($stringQuery);
    }

    protected abstract function getChangeColumn(SqlChangeColumnType $parameter=null);

    protected abstract function getAddColumn(SqlAddColumn $parameter=null);
    
    protected function getAlterTableQuery(SqlAlterTableQuery $query)
    {
        $stringQuery = 'ALTER TABLE' . ' ' ;
        $stringQuery .=
            $this->getSchemaPrefixedTableName(
                $query->getTable()
            ) . ' ' ;

        $first=true ;
        foreach ($query->getParameters() as $parameter) {
            if (!$first) {
                $stringQuery .= ', ' ;
            } else {
                $first = false ;
            }

            if ($parameter instanceof SqlChangeColumnType) {
                $stringQuery .= $this->getChangeColumn($parameter);
            } else if ($parameter instanceof SqlAddColumn) {
                $stringQuery .= $this->getAddColumn($parameter);
            }
        }

        return trim($stringQuery);
    }

    protected function getCreateTriggerQuery(SqlCreateTriggerQuery $query)
    {
        $stringQuery = 'CREATE OR REPLACE RULE' . ' ' ;

        //Name
        $stringQuery .=
            $this->getVarDelimiter() .
            $query->getName() .
            $this->getVarDelimiter() . ' ' ;

        //Table
        $stringQuery .=
            'AS ON ' . $query->getTriggerType() .
            ' TO ' . $this->getTable($query->getTable(), false) . ' ' ;

        //Mode
        $stringQuery .=
            'DO ' . $query->getMode() . ' ' ;

        //Action
        $stringQuery .= $this->querySwitch($query->getAction());

        return trim($stringQuery);
    }

    protected final function querySwitch(SqlQuery $query)
    {

        $stringQuery = null ;
        if (!is_null($query)) {
            switch($query->getType()) {
            case SqlQuery::$select :
                $stringQuery = $this->getSelectQuery($query);
                break ;
            case SqlQuery::$insert :
                $stringQuery = $this->getInsertQuery($query);
                break ;
            case SqlQuery::$update :
                $stringQuery = $this->getUpdateQuery($query);
                break ;
            case SqlQuery::$delete :
                $stringQuery = $this->getDeleteQuery($query);
                break ;
            case SqlQuery::$createTable :
                $stringQuery = $this->getCreateTableQuery($query);
                break ;
            case SqlQuery::$createView :
                $stringQuery = $this->getCreateViewQuery($query);
                break ;
            case SqlQuery::$dropTable :
                $stringQuery = $this->getDeleteTableQuery($query);
                break ;
            case SqlQuery::$dropView :
                $stringQuery = $this->getDeleteViewQuery($query);
                break ;
            case SqlQuery::$alterTable :
                $stringQuery = $this->getAlterTableQuery($query);
                break ;
            case SqlQuery::$createTrigger :
                $stringQuery = $this->getCreateTriggerQuery($query);
                break ;
            }
        }
        
        return $stringQuery ;
    }

    public final function toString()
    {
        return $this->querySwitch($this->_sqlQuery);
    }
}
